/**
 * TextBox组,可以添加或者删除TextBox控件
 * @file InputGroup.js
 * @author chuzhenyang(chuzhenyang@baidu.com)
 */

define(function (require) {
    require('esui/TextBox');
    require('esui/Validity');

    var u = require('underscore');
    var esui = require('esui');
    var lib = require('esui/lib');
    var Control = require('esui/Control');
    var eoo = require('eoo');
    var $ = require('jquery');
    var painters = require('esui/painters');

    /**
     * TextBox组,可以添加或者删除TextBox控件
     *
     * @extends {Control}
     * @param {Object} options 初始化参数
     * @constructor
     */
    var InputGroup = eoo.create(
        Control,
        {
            /**
             * 控件类型,始终为`"InputGroup"`
             *
             * @type {string}
             * @readonly
             * @override
             */
            type: 'InputGroup',

            /**
             * 初始化参数
             *
             * @param {Object} [options] 构造函数传入的参数
             * @protected
             * @override
             */
            initOptions: function (options) {
                var properties = {
                    /**
                     * @property {number} [maxCount=9999]
                     *
                     * 可添加的最大的TextBox的数目,由于实际场景中不可能添加到9999个,所以默认值使用9999
                     * 表示没有数量限制
                     */
                    maxCount: 9999,
                    /**
                     * @property {number} [width=200]
                     *
                     * 默认宽度,此宽度实际上代表的是TextBox的宽度,对于本控件而言,默认不提供一个属性控制自身的宽度
                     */
                    width: 200,
                    /**
                     * @property {String} [addIconText='添加']
                     *
                     * 添加按钮显示的文本
                     */
                    addIconText: '添加',
                    /**
                     * @property {String} [deleteIconText='']
                     *
                     * 每一个新添加的TextBox右侧删除按钮的显示文本,默认是显示一个'X'号
                     */
                    deleteIconText: '',
                    /**
                     * @property {String|Object} [subRules='']
                     *
                     * 每一个TextBox的验证规则,默认是不设置任何验证的,但如果想要设置验证,可以传值给subRules
                     *
                     * 假设要设置两个验证规则,一个是必填,另一个是有一个最小长度是5个字符
                     * 有以下两种方式
                     *      1. subRules是一个对象,即:
                     *         subRules = {
                     *              required: 'required',
                     *              minLength: 10
                     *         }
                     *      2. subRules是一个JSON数据,即:
                     *         subRules = '{"required":"required","minLength":10}'
                     *
                     */
                    subRules: null,
                    /**
                     * @property {String} [name='']
                     *
                     * 输入控件的名称,用于表单提交时作为键值
                     */
                    name: '',
                    /**
                     * @property {Array} [datasource=['']]
                     *
                     * 控件的数据源,会根据数据源的个数及值渲染相应个数的TextBox并设置其值
                     */
                    datasource: ['']
                };

                var subRules =  options.subRules;

                // 这里会判断传入的subRules的类别,最终都转化为一个对象格式
                if (u.isObject(subRules)) {
                    options.subRules = subRules;
                }
                else if (u.isString(subRules)) {
                    options.subRules = $.parseJSON(subRules);
                }

                u.extend(properties, options);

                this.setProperties(properties);
            },

            /**
             * 初始化DOM结构
             *
             * @protected
             * @override
             */
            initStructure: function () {
                var helper = this.helper;

                var tpl = [
                    '<div id="${bodyId}" class="${bodyClass}"></div>',
                    '<a id="${addIconId}" class="${addIconClass}">${addIconText}</a>'
                ].join('');

                var mainElement = this.main;

                mainElement.innerHTML = lib.format(
                    tpl,
                    {
                        bodyId: helper.getId('body'),
                        bodyClass: helper.getPartClasses('body'),
                        addIconId: helper.getId('addIcon'),
                        addIconClass: helper.getPartClasses('addIcon'),
                        addIconText: this.get('addIconText')
                    }
                );
            },

            /**
             * 重渲染
             *
             * @method
             * @protected
             * @override
             */
            repaint: painters.createRepaint(
                Control.prototype.repaint,
                {
                    name: ['datasource'],
                    paint: function (inputGroup, datasource) {
                        // 根据数据源的个数及当前已有的TextBox控件的个数计算出差值,从而少加多删
                        if (!datasource) {
                            datasource = [''];
                        }
                        var inputNum = datasource.length - inputGroup.children.length;

                        for (var i = 0; i < inputNum; i++) {
                            addInput.call(inputGroup);
                        }

                        for (i = 0; i > inputNum; i--) {
                            inputGroup.deleteInput();
                        }

                        // 然后将每个datasource里的值分别设置到相应顺序的TextBox中
                        u.each(inputGroup.children, function (child, index) {
                            child.setValue(datasource[index]);
                        });
                    }
                },
                {
                    name: ['disabled'],
                    paint: function (inputGroup, disabled) {
                        u.each(inputGroup.children, function (child) {
                            child.setProperties({disabled: disabled});
                        });
                    }
                }
            ),

            /**
             * 初始化事件交互
             *
             * @protected
             * @override
             */
            initEvents: function () {
                var me = this;

                // 为添加按钮添加点击事件,没点击一次,增加一行
                this.helper.addDOMEvent(
                    this.helper.getPart('addIcon'),
                    'click',
                    function (event) {
                        addInput.call(me);
                    }
                );

                // 为删除按钮添加点击事件,根据删除按钮的data-input-index属性来判断删除第几个
                this.helper.addDOMEvent(
                    this.helper.getPart('body'),
                    'click',
                    '[data-input-index]',
                    function (event) {
                        var index = +$(event.target).attr('data-input-index');

                        me.deleteInput(index);
                    }
                );

                // 监听每次删除和添加TextBox的事件,以此控制添加按钮的显示和隐藏
                this.on('inputchange', function (e) {
                    var addIconHref = $(this.helper.getPart('addIcon'));

                    if (this.children.length >= this.maxCount) {
                        addIconHref.hide();
                    }
                    else {
                        addIconHref.show();
                    }
                }, this);
            },


            /**
             * 暴露一个删除Input的接口 默认从0开始
             * 如果某些场合下需要手动删除某个位置下的TextBox可以调用此方法
             *
             * @param {number} index 要删除的TextBox的索引
             *
             */
            deleteInput: function (index) {
                removeInput.call(this, index);

                // 每次删除完TextBox后由于所以会发生变化,因此手动在这里将index重新排列一遍
                var deleteIcons = $(this.helper.getPart('body')).find('[data-input-index]');
                u.each(deleteIcons, function (icon, iconIndex) {
                    // 由于第一个的TextBox是不能被删除的,而且第一个TextBox后侧并没有
                    // 带有data-input-index索引的删除按钮,因此这里要加上1
                    $(icon).attr('data-input-index', iconIndex + 1);
                });
            }
        }
    );

    /**
     * 添加一个TextBox到InputGroup的方法
     *
     */
    function addInput() {
        // 如果当前已加入的TextBox的数目大于等于最大数目,直接返回
        if (this.maxCount <= this.children.length) {
            return;
        }

        var inputTpl = [
            '<div class="${inputContainerClass}">',
                '<span class="${deleteIconClass}" data-input-index="${index}">${deleteIconText}</span>',
            '</div>'
        ];

        // 对于第一个TextBox而言,是不能被删除的,因此此时将delete的模板删除掉
        if (this.children.length === 0) {
            inputTpl.splice(1, 1);
        }

        var inputContainerNode = $(lib.format(
            inputTpl.join(''),
            {
                inputContainerClass: this.helper.getPartClasses('inputcontainer'),
                width: this.get('width'),
                deleteIconClass: this.helper.getIconClass() + '-remove',
                deleteIconText: this.deleteIconText,
                index: this.children.length
            }
        ));

        var textBox = esui.create('TextBox', u.extend({
            mode: 'text',
            width: this.width,
            name: this.name
        }, this.subRules));

        // 在这里,由于TextBox的验证信息应该放在删除按钮的后面,不能按照默认的显示方式直接append到父元素的末尾
        // 因此,这里需要将textBox的validityLabel属性设置为验证Label的id
        textBox.set('validityLabel', textBox.id + '-validity');

        // 而所要添加的Valdity的id直接设置为textbox的validityLabel属性
        var validityLabel = esui.create('Validity', {
            id: textBox.validityLabel
        });


        $(this.helper.getPart('body')).append(inputContainerNode);

        // 要进行一下判断,如果是第一个TextBox,由于没有删除按钮,所以直接appendTo到父元素即可
        // 但对于有删除按钮的TextBox,则需要使用insertBefore插入到删除按钮的前面
        if (this.children.length === 0) {
            textBox.appendTo(inputContainerNode[0]);
        }
        else {
            textBox.insertBefore($(inputContainerNode[0]).children()[0]);
        }
        // 将新生成的textBox加入到InputGroup的子控件中
        this.addChild(textBox);

        validityLabel.appendTo(inputContainerNode[0]);
        // 把validityLabel添加作为TextBox的子控件,这样不用再去考虑删除textBox的时候validityLabel的dispose过程
        textBox.addChild(validityLabel);

        this.fire('inputchange');
    }

    /**
     * 删除一个TextBox,并将容器节点删除
     *
     * @param {number} index 要删除的TextBox的索引
     *
     */
    function removeInput(index) {
        // 如果不传index或者index为负数,则默认删除最后一个
        if (index === undefined || index < 0) {
            index = this.children.length - 1;
        }
        var textBox = this.children[index];

        // 假如计算出要删除第一个或者数目过大,直接返回
        if (index === 0 || !textBox) {
            return;
        }

        this.removeChild(textBox);
        // 要移除父节点元素
        $(textBox.main).parent().remove();
        textBox.dispose();

        this.fire('inputchange');
    }

    esui.register(InputGroup);
    return InputGroup;
});
